/** Copyright Notice:

    Copyright (C) 2011 Ivo Wingelaar

    This software is provided 'as-is', without any express or implied
    warranty.  In no event will the authors be held liable for any damages
    arising from the use of this software.

    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute it
    freely, subject to the following restrictions:

    1.  The origin of this software must not be misrepresented; you must not
        claim that you wrote the original software. If you use this software
        in a product, an acknowledgment in the product documentation would be
        appreciated but is not required.
    2.  Altered source versions must be plainly marked as such, and must not be
        misrepresented as being the original software.
    3.  This notice may not be removed or altered from any source distribution.

    Please note that the Myth Library is based in part on the work of Nikolaus
    Gebhardt (Irrlicht Engine) and I give full credits to him, and the people who
    helped him for making available an excellent piece of code.

**/

//
// MythCryptoHashFunctionStub_C.cpp 010: implementation of the stub hash algorithm
//

#include "MythCrypto_HashFunctionStub_C.hpp"

namespace Myth
{
namespace Crypto
{

/** constructor **/
/// \param   uint32_t hash_size
///         Size of the hash in bytes.
/// \param   uint32_t transform_size
///         Size of a single block of data.
HashFunctionStub_C::HashFunctionStub_C( uint32_t hash_size,
                                        uint32_t transform_size) :
    mProcessed(0),
    mHashSize(hash_size),
    mTransformSize(transform_size)
{
    mTransformBuffer = new uint8_t[mTransformSize];
}

/** destructor **/
HashFunctionStub_C::~HashFunctionStub_C()
{
    delete [] mTransformBuffer;
}

/** virtual function **/
/** reset_b          **/
//! Resets a few variables in the stub.
/// \return void
/// \param  void
void HashFunctionStub_C::reset_b(void)
{
    mProcessed = 0;
}

/** virtual function **/
/** feed             **/
//! Feeds a new piece of data in the digester.
/// \return void
/// \param   uint8_t *data
///         Pointer to a piece of data.
/// \param   size_t size
///         The size of the piece of data.
void HashFunctionStub_C::feed( uint8_t *data,
                               size_t size)
{
    uint32_t buffered = mProcessed & (mTransformSize - 1);
    uint32_t padding = mTransformSize - buffered;

    mProcessed += size;

    if(buffered && size >= padding)
    {
        memcpy(mTransformBuffer + buffered, data, padding);
        transform_block(mTransformBuffer);

        data += padding;
        size -= padding;
        buffered = 0;
    }

    while(size >= mTransformSize)
    {
        transform_block(data);
        data += mTransformSize;
        size -= mTransformSize;
    }

    if(size > 0)
        memcpy(mTransformBuffer + buffered, data, size);
}

/** const virtual function **/
/** get_hash_size          **/
//! Returns the length of the hash.
/// \return uint32_t
///         The length of the hash.
/// \param  void
uint32_t HashFunctionStub_C::get_hash_size(void) const
{
    return mHashSize;
}

/** const virtual function **/
/** get_transform_size     **/
//! Returns the size of the transformation buffer.
/// \return uint32_t
///         The size of the transformation buffer.
/// \param  void
uint32_t HashFunctionStub_C::get_transform_size(void) const
{
    return mTransformSize;
}

/** virtual function **/
/** get_hash_hex     **/
//! Returns the hash, in hex.
/// \return void
/// \param   char *hash
///         A pointer to a location where you want to store the hash, in hex.
void HashFunctionStub_C::get_hash_hex( char *hash)
{
    uint8_t *h = new uint8_t[get_hash_size()];
    get_hash(h);

    binary_to_hex(h, hash, get_hash_size());

    delete h;
}

/** virtual function **/
/** auto_hash        **/
//! Automaticly hashes data and returns its hash.
/// \return void
/// \param   uint8_t *data
///         Pointer to a piece of data.
/// \param   size_t bytes
///         The size of the block of data.
/// \param   uint8_t *hash
///         A pointer to a location where you want to store the hash.
void HashFunctionStub_C::auto_hash( uint8_t *data,
                                    size_t bytes,
                                    uint8_t *hash)
{
    reset();
    feed(data, bytes);
    finish();

    get_hash(hash);
    reset();
}

/** virtual function **/
/** auto_hash_hex    **/
//! Automaticly hashes data and returns its hash in hex-form.
/// \return void
/// \param   uint8_t *data
///         Pointer to a piece of data.
/// \param   size_t bytes
///         The size of the block of data.
/// \param   char *hash
///         A pointer to a location where you want to store the hash, in hex.
void HashFunctionStub_C::auto_hash_hex( uint8_t *data,
                                        size_t bytes,
                                        char *hash)
{
    uint8_t *h = new uint8_t[get_hash_size()];
    auto_hash(data, bytes, h);

    binary_to_hex(h, hash, get_hash_size());

    delete h;
}

/** virtual function **/
/** auto_hash        **/
//!  Automaticly hashes a stream and returns its hash.
/// \return void
/// \param   Stream::Input *stream
///         The stream you want to hash.
/// \param   uint32_t chunkSize
///         The size of the chunks that will be loaded.
/// \param   uint8_t *hash
///         A pointer to a location where you want to store the hash.
void HashFunctionStub_C::auto_hash( Stream::Input *stream,
                                    uint32_t chunkSize,
                                    uint8_t *hash)
{
    uint8_t *buffer = new uint8_t[chunkSize];

    reset();

    int n;
    while((n = stream->read(buffer, chunkSize)) > 0)
        feed(buffer, n);

    finish();

    get_hash(hash);
    reset();
}

/** virtual function **/
/** auto_hash_hex    **/
//! Automaticly hashes a stream and returns its hash in hex-form.
/// \return void
/// \param   Stream::Input *stream
///         The stream you want to hash.
/// \param   uint32_t chunkSize
///         The size of the chunks that will be loaded.
/// \param   char *hash
///         A pointer to a location where you want to store the hash, in hex.
void HashFunctionStub_C::auto_hash_hex( Stream::Input *stream,
                                        uint32_t chunkSize,
                                        char *hash)
{
    uint8_t *h = new uint8_t[get_hash_size()];
    auto_hash(stream, chunkSize, h);

    binary_to_hex(h, hash, get_hash_size());

    delete h;
}

/** const virtual function **/
/** hash_equal             **/
//! Checks if the supplied hashes are equal.
/// \return bool
///         True if the hashes are equal, false otherwise.
/// \param   uint8_t* a
///         The first hash.
/// \param   uint8_t* b
///         The second hash.
bool HashFunctionStub_C::hash_equal( uint8_t *a,
                                     uint8_t *b) const
{
    for(uint32_t i = 0; i < get_hash_size(); i++)
        if(a[i] != b[i])
            return false;

    return true;
}

/** const virtual function **/
/** get_processed          **/
//! Returns how much data was processed.
/// \return uint64_t
///         How much data was processed.
/// \param  void
uint64_t HashFunctionStub_C::get_processed(void) const
{
    return mProcessed;
}

} // namespace Crypto
} // namespace Myth
